package com.xhj.mybatis.mapper;

import com.xhj.mybatis.annotations.Param;
import com.xhj.mybatis.entity.User;
import com.xhj.mybatis.mapping.BoundSql;
import com.xhj.mybatis.mapping.MappedStatement;
import com.xhj.mybatis.scripting.xmltags.SqlNode;
import com.xhj.mybatis.session.Configuration;
import com.xhj.mybatis.session.ResultHandler;
import com.xhj.mybatis.session.SqlSession;
import org.dom4j.Element;

import java.lang.reflect.Method;
import java.sql.PreparedStatement;
import java.sql.Statement;
import java.util.List;
import java.util.Map;

/**
 * UserMapper
 *
 * @author XJks
 * @description 用户实体类的Mapper接口
 */
public interface UserMapper {

    User selectOneById(Long id);

    List<User> selectAll();

    /**
     * TODO:
     *  <pre>
     *  1.注意该方法参数名称与 XxxMapper.xml 中的参数名称并不一致，但是可以通过 @Param 注解进行映射。
     *  2.在对应的 XxxMapper.xml 中，该方法对应的{@code <select>}标签如下:
     *      <pre>
     *          {@code
     *              <select id="selectByIdAndUsername" resultType="com.xhj.mybatis.entity.User">
     *                      SELECT *
     *                      FROM user
     *                      where id = #{id}
     *                      AND username = #{username}
     *              </select>
     *          }
     *      </pre>
     *  3.相关执行链过程分析:
     *      3.1 创建 Configuration 全局配置流程，主要得到 MappedStatement 集合。
     *          3.1.1  {@link com.xhj.mybatis.builder.xml.XMLConfigBuilder#parse()}
     *          3.1.2  {@link com.xhj.mybatis.builder.xml.XMLConfigBuilder#mapperElement(Element mappers)}
     *          3.1.3  {@link com.xhj.mybatis.builder.xml.XMLMapperBuilder#parse()}
     *          3.1.4  {@link com.xhj.mybatis.builder.xml.XMLMapperBuilder#configurationElement(Element element)}
     *          3.1.5  {@link com.xhj.mybatis.builder.xml.XMLMapperBuilder#buildStatementFromContext(java.util.List<Element> list)}
     *          3.1.6  {@link com.xhj.mybatis.builder.xml.XMLStatementBuilder#parseStatementNode()}:
     *                      (1) 在该方法中会解析 XxxMapper.xml 中的 {@code <select>} 标签，其中会解析其中的 parameterType，获取到的 parameterTypeClass 为 null。
     *                      (2) 该方法之后会执行 {@code SqlSource sqlSource = langDriver.createSqlSource(configuration, element, parameterTypeClass)} 语句创建 SqlSource 对象，处理参数类型为 null 的情况，接下来进入对应方法进行解析。
     *          3.1.7  {@link com.xhj.mybatis.scripting.xmltags.XMLLanguageDriver#createSqlSource(Configuration configuration, Element script, Class<?> parameterType)}
     *          3.1.8  {@link com.xhj.mybatis.scripting.xmltags.XMLScriptBuilder#parseScriptNode()}:
     *                      (1) 在该方法中会执行 {@code new RawSqlSource(configuration, rootSqlNode, parameterType)} 语句创建 SqlSource对象，继续处理参数类型为 null 的情况，接下来进入对应方法进行解析。
     *          3.1.9  {@link com.xhj.mybatis.scripting.defaults.RawSqlSource#RawSqlSource(Configuration configuration, SqlNode rootSqlNode, Class<?> parameterType)}
     *          3.1.10 {@link com.xhj.mybatis.scripting.defaults.RawSqlSource#RawSqlSource(Configuration configuration, String sql, Class<?> parameterType)}:
     *                      (1) 在该方法中会执行 {@code Class<?> clazz = parameterType == null ? Object.class : parameterType} 真正正处理参数类型为 null 的情况，将参数类型替换为 Object.class。
     *                      (2) 接下来会执行 {@code sqlSource = sqlSourceParser.parse(sql, clazz, new HashMap<>())} 语句创建 StaticSqlSource 对象，接下来进入对应方法进行解析。
     *          3.1.11 {@link com.xhj.mybatis.builder.SqlSourceBuilder#parse}:
     *                      (1) 在该方法中会执行 {@code ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType, additionalParameters)} 语句创建 ParameterMappingTokenHandler 对象，传入的 parameterType 为 Object.class。
     *                      (2) 接下来会执行 {@code sql = parser.parse(removeExtraWhitespaces(originalSql))} 或 {@code sql = parser.parse(originalSql)} 语句对 SQL 进行解析，接下来进入对应方法进行解析。
     *          3.1.12 {@link com.xhj.mybatis.parsing.GenericTokenParser#parse(String text)}:
     *                      (1) 在该方法中会循环调用 {@code handler.handleToken(expression.toString())} 方法对 SQL 语句中的每个占位符 {@code #{id}} 和 {@code #{username}}进行处理，接下来进入对应方法进行解析。
     *          3.1.13 {@link com.xhj.mybatis.builder.SqlSourceBuilder.ParameterMappingTokenHandler#handleToken(String content)}:
     *                      (1) 在该方法中会执行 {@code parameterMappings.add(buildParameterMapping(content))} 语句为对应占位符里面的参数名称 {@code id} 或 {@code username} 创建 ParameterMapping 对象，接下来进入对应方法进行解析。
     *          3.1.14 {@link com.xhj.mybatis.builder.SqlSourceBuilder.ParameterMappingTokenHandler#buildParameterMapping(String content)}:
     *                      (1) 在 XxxMapper.xml 中的 {@code <select>} 标签中， parameterType 属性确定的是整个方法参数列表的整体包装类型。如果方法参数只有一个，则可以填写对应方法参数的类型。如果方法参数有多个，则不填(在真正的 mybatis 框架中，应该可以填实体类类型)。
     *                      (2) 因为 parameterType 确定的是方法参数列表的整体包装类型，但还没有对方法参数列表中的单个参数进行解析类型，所以在该方法需要通过相关逻辑处理确定单个参数的类型，然后才能够为对应占位符里面的参数名称 {@code id} 或 {@code username} 创建 ParameterMapping 对象，然后为单个参数确定对应的 TypeHandler。
     *          3.1.15 {@link com.xhj.mybatis.mapping.ParameterMapping.Builder#build()}:
     *                      (1) 该方法会执行 {@code resolveTypeHandler()}方法，为 ParameterMapping 解析对应的类型处理器，接下来进入对应方法进行解析。
     *          3.1.16 {@link com.xhj.mybatis.mapping.ParameterMapping.Builder#resolveTypeHandler()}:
     *                      (1) 该方法可能会执行 {@code parameterMapping.typeHandler = typeHandlerRegistry.getTypeHandler(parameterMapping.javaType, parameterMapping.jdbcType)} 语句，为 parameterMapping 的类型处理器赋值。
     *                      (2) 在解析 {@link #selectByIdAndUsername(Long, String)} 方法时，由于方法参数列表整体类型是 Object 类型，则这里得到的是 {@link com.xhj.mybatis.type.UnknownTypeHandler} 类型处理器。
     *      3.2 使用 SqlSession 获得 XxxMapper.class 的代理对象。
     *          执行 {@code UserMapper userMapper = sqlSession.getMapper(UserMapper.class)} 语句，获取对应 UserMapper 的代理对象，接下来进入相关方法进行解析。
     *          3.2.1  {@link com.xhj.mybatis.session.defaults.DefaultSqlSession#getMapper(Class<T> mapperClass)}
     *          3.2.2  {@link com.xhj.mybatis.session.Configuration#getMapper(Class, SqlSession)}
     *          3.2.3  {@link com.xhj.mybatis.binding.MapperRegistry#getMapper(Class, SqlSession)}:
     *                      (1) 在该方法会执行 {@code mapperProxyFactory.newInstance(sqlSession)} 语句，处理对应 Mapper 代理对象的创建逻辑，接下来进入相关方法进行解析。
     *          3.2.4  {@link com.xhj.mybatis.binding.MapperProxyFactory#newInstance(SqlSession sqlSession)}:
     *                      (1) 在该方法中会执行 {@code Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, mapperProxy)}语句，调用 JDK 动态代理 API 生成代理对象。
     *      3.3 XxxMapper.class 对应方法的执行逻辑。
     *          3.3.1  {@link #selectByIdAndUsername(Long _id, String _username)}
     *          3.3.2  {@link com.xhj.mybatis.binding.MapperProxy#invoke(Object proxy, Method method, Object[] args)}:
     *                      (1) 在该方法中会执行 {@code MapperMethod mapperMethod = cachedMapperMethod(method)} 语句，获取对应封装好的 MapperMethod 对象，接下来进入对应方法进行解析。
     *          3.3.3  {@link com.xhj.mybatis.binding.MapperProxy#cachedMapperMethod(Method method)}:
     *                      (1) 在该方法中可能会执行 {@code mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration())} 语句，创建封装好的 MapperMethod 对象，接下来进入对应方法进行解析。
     *          3.3.4  {@link com.xhj.mybatis.binding.MapperMethod#MapperMethod(Class mapperInterface, Method method, Configuration configuration)}:
     *                      (1) 在该方法中会执行 {@code this.method = new MethodSignature(configuration, method)}语句，创建 MethodSignature 对象，该对象封装了相关方法的参数类型等信息，接下来进入对应方法进行解析。
     *          3.3.5  {@link com.xhj.mybatis.binding.MapperMethod.MethodSignature#MethodSignature(Configuration configuration, Method method)}
     *          3.3.6  {@link com.xhj.mybatis.reflection.ParamNameResolver#ParamNameResolver(Configuration config, Method method)}:
     *                      (1) 在该方法中，会获取 XxxMapper.class 对应方法的有序参数名称列表。
     *                      (2) 执行到这里后，回到 3.3.3 {@link com.xhj.mybatis.binding.MapperProxy#cachedMapperMethod(Method method)}方法，下一步执行 {@code mapperMethod.execute(sqlSession, args)} 语句，通过封装的 MapperMethod 对象触发相关执行逻辑，接下来进入对应方法进行解析。
     *          3.3.7  {@link com.xhj.mybatis.binding.MapperMethod#execute(SqlSession sqlSession, Object[] args)}:
     *                      (1) 在该方法中，会执行 {@code Object param = method.convertArgsToSqlCommandParam(args)}语句，将整个方法参数列表封装为一个 Object 对象，接下来进入对应方法进行解析。
     *          3.3.8  {@link com.xhj.mybatis.reflection.ParamNameResolver#getNamedParams(Object[] args)}:
     *                      (1) 在该方法中，会对 XxxMapper.class 对应方法的实际输入参数列表进行包装。
     *                      (2) 如果方法参数列表只有一个参数，则从数组取值并返回，去一层包装。
     *                      (3) 如果方法参数列表有多个参数，则封装为一个 Map 对象返回。
     *                      (4) 执行到这里后，回到 3.3.7  {@link com.xhj.mybatis.binding.MapperMethod#execute(SqlSession sqlSession, Object[] args)}方法，下一步执行 {@code result = sqlSession.selectOne(command.getName(), param)} 语句，接下来进入对应方法进行解析。
     *          3.3.9  {@link com.xhj.mybatis.session.defaults.DefaultSqlSession#selectOne(String statement, Object parameter)}
     *          3.3.10 {@link com.xhj.mybatis.session.defaults.DefaultSqlSession#selectList(String statement, Object parameter)}:
     *                      (1) 该方法中首先会执行 {@code MappedStatement ms = configuration.getMappedStatement(statement)} 语句，获取对应的 MappedStatement 对象。
     *                      (2) 该方法中然后会执行 {@code executor.query(ms, parameter, Executor.NO_RESULT_HANDLER)} 语句，使用对应执行器执行 SQL 语句，接下来进入对应方法进行解析。
     *          3.3.11 {@link com.xhj.mybatis.executor.BaseExecutor#query(MappedStatement ms, Object parameter, ResultHandler resultHandler)}:
     *                      (1) 该方法首先会执行 {@code ms.getBoundSql(parameter)}语句，获取实际执行过程中 BoundSql 对象，该对象主要封装的是 parameterMappings 和 parameterObject。
     *                      (2) parameterMappings 封装的是 XxxMapper.class 对应方法的有序参数列表信息，是固定值。
     *                      (3) parameterObject 封装的是 XxxMapper.class 对应方法的实际输入参数列表，是动态值。
     *          3.3.12 {@link com.xhj.mybatis.executor.BaseExecutor#query(MappedStatement ms, Object parameter, ResultHandler resultHandler, BoundSql boundSql)}
     *          3.3.13 {@link com.xhj.mybatis.executor.SimpleExecutor#doQuery(MappedStatement ms, Object parameter, ResultHandler resultHandler, BoundSql boundSql)}:
     *                      (1) 该方法会执行 {@code StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, resultHandler, boundSql)}语句，创建 PreparedStatementHandler 对象。
     *                      (2) 创建 PreparedStatementHandler 对象时，会调用 {@link com.xhj.mybatis.executor.statement.BaseStatementHandler#BaseStatementHandler}构造函数，然后执行以下方法初试化参数处理器 parameterHandler 和结果集处理器 resultSetHandler。
     *                      (3) 该方法然后会执行 {@code handler.parameterize(stmt)}语句，设置 SQL 语句的参数，接下来进入对应方法进行解析。
     *          3.3.14 {@link com.xhj.mybatis.executor.statement.PreparedStatementHandler#parameterize(Statement statement)}
     *          3.3.15 {@link com.xhj.mybatis.scripting.defaults.DefaultParameterHandler#setParameters(PreparedStatement ps)}:
     *                      (1) 通过 3.1.16 {@link com.xhj.mybatis.mapping.ParameterMapping.Builder#resolveTypeHandler()} 解析得到的 typeHandler，然后和根据对应 ParameterMapping 和整个方法参数列表包装对象 parameterObject 设置参数。
     *                      (2) 如果是方法参数列表是多个参数，大概率会执行其中的 {@code MetaObject metaObject = configuration.newMetaObject(parameterObject)} 语句获取 MetaObject 对象，然后执行 {@code value = metaObject.getValue(propertyName)} 语句获取对应属性值。
     *  <pre/>
     */
    User selectOneByIdAndUsername(@Param("id") Long _id, @Param("username") String _username);

    // -------------------------------- selectByUsername --------------------------------
    User selectOneByUsername(String username);

    List<User> selectListByUsername(String username);

    // -------------------------------- selectByBooleanColumn --------------------------------
    List<User> selectListByBooleanColumn(Boolean booleanColumn);

    // -------------------------------- selectByByteColumn --------------------------------
    User selectOneByByteColumn(Byte byteColumn);

    List<User> selectListByByteColumn(Byte byteColumn);

    // -------------------------------- selectByShortColumn --------------------------------
    User selectOneByShortColumn(Short shortColumn);

    List<User> selectListByShortColumn(Short shortColumn);

    // -------------------------------- selectByLongColumn --------------------------------
    User selectOneByIntegerColumn(Integer integerColumn);

    List<User> selectListByIntegerColumn(Integer integerColumn);

    // -------------------------------- selectByLongColumn --------------------------------
    User selectOneByLongColumn(Long longColumn);

    List<User> selectListByLongColumn(Long longColumn);

    // -------------------------------- selectByFloatColumn --------------------------------
    User selectOneByFloatColumn(Float floatColumn);

    List<User> selectListByFloatColumn(Float floatColumn);

    // -------------------------------- selectByDoubleColumn --------------------------------
    User selectOneByDoubleColumn(Double doubleColumn);

    List<User> selectListByDoubleColumn(Double doubleColumn);

    // -------------------------------- selectByStringColumn --------------------------------
    User selectOneByStringColumn(String stringColumn);

    List<User> selectListByStringColumn(String stringColumn);

    // -------------------------------- 测试 select 类型的动态SQL语句 --------------------------------
    List<User> selectListByConditionWithEntityParameterAndUserStatusList(@Param("user") User user, @Param("userStatusList") List<Integer> userStatusList, @Param("enableCondition") Boolean enableCondition);

    // -------------------------------- insertOne --------------------------------
    int insertOneBySeparateParameters(Long id, String username, Integer status, Boolean booleanColumn, Byte byteColumn, Short shortColumn, Integer integerColumn, Long longColumn, Float floatColumn, Double doubleColumn, String stringColumn);

    int insertOneByEntityParameter(User user);

    int insertOneByMapParameter(Map<String, Object> params);

    int insertOneByEntityParameterAndOtherParmForBeforeSelectKeyGenerator(@Param("user") User user, @Param("otherParam") String otherParam);

    int insertOneByEntityParameterAndOtherParmForAfterSelectKeyGenerator(@Param("user") User user, @Param("otherParam") String otherParam);

    int insertOneByEntityParameterAndOtherParmForJdbc3KeyGenerator(@Param("user") User user, @Param("otherParam") String otherParam);

    // -------------------------------- deleteAll --------------------------------
    int deleteAll();

    // -------------------------------- deleteListByUsername --------------------------------
    void deleteListByUsernameForVoidReturnType(String username);

    int deleteListByUsernameForIntegerReturnType(String username);

    long deleteListByUsernameForLongReturnType(String username);

    boolean deleteListByUsernameForBooleanReturnType(String username);

    // -------------------------------- updateUsernameById --------------------------------
    int updateUsernameByIdAndSeparateParameters(Long id, String username);

    int updateUsernameByIdAndEntityParameter(User user);

    int updateUsernameByIdAndMapParameter(Map<String, Object> params);

    int updateByConditionWithEntityParameterAndUserStatusList(@Param("user") User user, @Param("userStatusList") List<Integer> userStatusList);
}
